package xfuzzy.alg_genetico.indeterminado.xfghlOld.model.spec;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

import xfuzzy.alg_genetico.indeterminado.xfghlOld.model.XfghlConfig;
import xfuzzy.alg_genetico.indeterminado.xfghlOld.model.hierStruct.XfghlHierStruct;
import xfuzzy.alg_genetico.indeterminado.xfghlOld.model.hierStruct.XfghlModule;
import xfuzzy.alg_genetico.indeterminado.xfghlOld.model.hierStruct.XfghlNode;
import xfuzzy.lang.Operatorset;
import xfuzzy.lang.RuleSet;
import xfuzzy.lang.RuleSetCall;
import xfuzzy.lang.Specification;
import xfuzzy.lang.SystemModule;
import xfuzzy.lang.Type;
import xfuzzy.lang.Universe;
import xfuzzy.lang.Variable;
import xfuzzy.lang.XflException;
import xfuzzy.xfds.XfdsAttribute;

/**
 * Clase que dada una estructura HierStruct y una configuraci�n nos crea una Specification
 * 
 * @author Sergio Cala Cordero, Septiembre 2009, Herramienta Xfghl
 *
 */

public class XfghlSpecification {
	
	/**
	 * Metodo estatico que nos genera una Specificacition dada una XfghlHierStruct y una XfghlConfig
	 * @param estrJerar Estructura jerarquica que queremos convertir a Specification
	 * @param config    Configuracion para crear la especificacion
	 * @return Specificacion creada con la estructura y la configuracion deseada
	 * @throws XflException
	 */
	
	public static synchronized Specification create(XfghlHierStruct estrJerar, XfghlConfig config) throws XflException
	{
		
		//Contadores de variables que empiezan desde el valor maximo
		int numVarInnerIn = estrJerar.getNumModulos() * 2;
		int numVarInnerOut = estrJerar.getNumModulos();		
		
		Specification specification = new Specification(config.getNameSystem());
	   
		//Sistema jerarquico de salida
		SystemModule systemModule = new SystemModule();		
		//Añadimos el sitema a la specification
		specification.setSystemModule(systemModule);
		
		//Conjunto de operadores
		Operatorset operSet = config.getOperSet();
		//Añadimos el operador al conjunto de operadores
		specification.addOperatorset(operSet);
				
		//Creamos los tipos de entrada		
		XfdsAttribute [] attribute= config.getDataSet().getInputAttributes();
		//Lista de tipos de entrada
		java.util.List<Type> listInType=new LinkedList<Type>();
		//Lista de atributos de entrada
		java.util.List<XfdsAttribute> listAttributeIn=new LinkedList<XfdsAttribute>();
		
		for (int i=0;i<attribute.length;i++) {									
			listInType.add(XfghlInputType.create(config.getStileInput(),"T"+attribute[i].getName(), attribute[i].getUniverse(),config.getMfs()));
			listAttributeIn.add(attribute[i]);			
		}					
	    
		
		/*Creamos el tipo de salida*/
		XfdsAttribute attributeOut=config.getDataSet().getOutputAttributes()[0];		
		Type outType=XfghlOutputType.create(config.getDefuzOperator(),"T"+attributeOut.getName(),attributeOut.getUniverse(),config.getMfs()*config.getMfs());							
	    
	    //Cola para guardar los Modulos
	    Queue<XfghlModule> colaModulos=new LinkedList<XfghlModule>();
	    //Pila para guardar las variables del sistema
	    Stack<Variable> pilaVarSystem=new Stack<Variable>();
	    
	    /*Encolamos el primer Modulo*/    
	    colaModulos.add((XfghlModule)estrJerar.getM());
	    
	    /*Apilamos la variable de salida del sistema*/	    
	    Variable vOut=new Variable(attributeOut.getName(),outType,Variable.OUTPUT);	    
	    pilaVarSystem.add(vOut);
	    //A�adimos la variable de salida del sistema
	    systemModule.addVariable(vOut);	    
	    	    
	    
	    /*Lista de llamadas a las base de datos para tenerlas ordenadas*/
	    LinkedList<RuleSetCall> listRuleSetCall=new LinkedList<RuleSetCall>(); 
	    /*Lista de tipos de la especificaci�n*/
	    LinkedList<Type> listTypes=new LinkedList<Type>();
	    /*Lista de variables de entrada al sistema*/
	    Variable [] arrayVariable=new Variable[config.getNumVarEnt()];
	    
	    /*A�adimos el tipo de salida*/
	    listTypes.add(outType);
	    
	    //Tipo de las variables de entrada internas
	    Type typeInnerIn=XfghlInputType.create(config.getStileInput(), "Name", new Universe(-1,1), config.getMfs());
	    Type typeInnerOut=XfghlOutputType.create(config.getDefuzOperator(), "Name", new Universe(-1,1), config.getMfs()*config.getMfs());
	    
	    while (!colaModulos.isEmpty()) {

	    	//Variables de la llamada al ruleSet
	    	Variable ent1;
	    	Variable ent2;
	    	
	    	Variable sal=pilaVarSystem.pop();
	    	
	    	//Desencolamos un m�dulo
	    	XfghlModule mod=colaModulos.remove();
	    	
	    	//Si la entrada 2 es un m�dulo 
		    if(((XfghlModule)(mod)).getEnt2() instanceof XfghlModule)
		    {
		    	//A�adimos una variable interna al sistema		    	
		    	ent2=systemModule.addInnerVariable();
		    	pilaVarSystem.add(ent2);
		    	colaModulos.add(((XfghlModule)((XfghlModule)(mod)).getEnt2()));
		    }
		    else //Si la entrada 2 es un nodo es una entrada
		    {   		    		
		    	//Sabemos que variable de entrada es con el valor del nodo
		    	int numEntrada=((XfghlNode)((XfghlModule)(mod)).getEnt2()).getValor();
		    	//Accdemos los datos de la variable de entrada
		    	Type typeIn=(Type)listInType.get(numEntrada);		    
			    XfdsAttribute attributeIn=listAttributeIn.get(numEntrada);
			    
			    typeIn.setName("T"+attributeIn.getName());
			    
			    //Nos creamos la variable de entrada
			    ent2=new Variable(attributeIn.getName(),typeIn,Variable.INPUT);
			    			    			    
			    /*A�adimos el tipo y la variable de entrada*/
			    listTypes.add(typeIn);
			    arrayVariable[numEntrada]=ent2;
		    }
	    	
	    	//Si la entrada 1 es un m�dulo 
		    if(((XfghlModule)(mod)).getEnt1() instanceof XfghlModule)
		    {
		    	//A�adimos una variable interna al sistema		    	
		    	ent1=systemModule.addInnerVariable();		    	
		    	pilaVarSystem.add(ent1);
		    	colaModulos.add(((XfghlModule)((XfghlModule)(mod)).getEnt1()));
		    }
		    else
		    {		    			    			    			    			   
		    	//Sabemos que variable de entrada es con el valor del nodo
		    	int numEntrada=((XfghlNode)((XfghlModule)(mod)).getEnt1()).getValor();
		    	//Accdemos los datos de la variable de entrada
		    	Type typeIn=(Type)listInType.get(numEntrada);		    
			    XfdsAttribute attributeIn=listAttributeIn.get(numEntrada);
			    
			    typeIn.setName("T"+attributeIn.getName());
			    
			    ent1=new Variable(attributeIn.getName(),typeIn,Variable.INPUT);
			    			    			    
			    /*A�adimos el tipo y la variable de entrada*/
			    listTypes.add(typeIn);			    
			    arrayVariable[numEntrada]=ent1;
		    }
	    		    		   		    
		    /*Creamos un nuevo RuleSet*/		    
		    //Variable 1
		    Type i1T;
		    if(ent1.getType()==null)
		    {
		    	i1T=(Type)(typeInnerIn.clone());
		    	i1T.setName("TI_"+numVarInnerIn);
			    //A�adimos el tipo de la variable 1
			    listTypes.add(i1T);
		    }
		    else{ //Variable de entrada del sistema
		    	i1T=((Type)ent1.getType());		    	
		    }
	    	Variable i1=new Variable("innI_"+numVarInnerIn,i1T,Variable.INPUT);
	    	numVarInnerIn--;
	    	
	    	//Variable 2
		    Type i2T;
		    if(ent2.getType()==null)
		    {
		    	i2T=(Type)(typeInnerIn.clone());
		    	i2T.setName("TI_"+numVarInnerIn);
			    //A�adimos el tipo de la variable 2
			    listTypes.add(i2T);

		    }
		    else{ //Variable de entrada del sistema
		    	i2T=((Type)ent2.getType());		    	
		    }
	    	Variable i2=new Variable("innI_"+numVarInnerIn,i2T,Variable.INPUT);
	    	numVarInnerIn--;


	    	//Variable salida		    
		    Type oT;
		    if(sal.getType()==null)
		    {
		    	oT=(Type)typeInnerOut.clone();
		        oT.setName("TO_"+numVarInnerOut);
			    //A�adimos el tipo de la variable de salida
			    listTypes.add(oT);
		    }
		    else{ //La variable de salida del sistema
		    	oT=((Type)sal.getType());
		    }		    		    
	    	Variable out=new Variable("innO_"+numVarInnerOut,oT,Variable.OUTPUT);
	    	numVarInnerOut--;		   
		    
	        //Nos creamos el RuleSet
		    RuleSet ruleSet=XfghlRuleSet.create("KB_"+mod.getIdMod(), i1, i2, out, operSet);		    
		    //Le asignamos la base de conocimiento a la variable de salida
		    out.setKnowledgeBase(ruleSet);
		    i1.setKnowledgeBase(ruleSet);
		    i2.setKnowledgeBase(ruleSet);
		    
		    Variable [] inputs={ent1,ent2};
		    Variable [] outputs={sal};
		    
		    listRuleSetCall.add(0,new RuleSetCall(ruleSet, inputs, outputs));
		    
		}
	    
	    /*Agregamos los tipos a la especificacion*/
	    for (Type type : listTypes) {
			specification.addType(type);
		}
	    
	    /*Agregamos todos los RuleSetCall y los RuleSet a la especificacion*/	    
	    for (RuleSetCall ruleSetCall : listRuleSetCall) {
	    	specification.addRulebase(ruleSetCall.getRulebase());
			systemModule.addCall(ruleSetCall.getRulebase(), ruleSetCall.getInputVariables(), ruleSetCall.getOutputVariables());
		}
	    
	    /*Agregamos las variables de entrada de al sistema*/
       for (Variable variable : arrayVariable) {
    	   systemModule.addVariable(variable);
       }            
       //System.out.println(specification.toXfl());
	   return specification;
	}
}
